# @Author:黄景涛
# @Date: 2024/2/20
"""
请求引擎，涵盖：请求参数获取与处理、发起请求、断言处理，   并日志记录
"""

import requests
import warnings
from faker import Faker

from utils.log.logger import logger
from codes.hot_load_engine import HotLoad
from codes.assert_engine import validate_check


class RequestUtil:
    session = requests.Session()
    UA = Faker(locale='zh-CN').user_agent()
    start_msg = "**********************************************接口测试开始**********************************************"
    end_msg = "**********************************************接口测试结束**********************************************\n"
    _instance = None
    f_name = 'extract.yaml'

    def __new__(cls, *args, **kwargs):
        """单实例控制 """
        if cls._instance is None:
            return super().__new__(cls)
        return cls._instance

    def __init__(self, case_info, test_module: object, test_class: object):
        """
        :param case_info: 原始yaml用例数据，包含method、headers、json、extract、validate等
        """
        self.case_name = case_info.get('name')  # 用例名称
        logger.info(f"用例名称：{self.case_name}")
        self.case_data = case_info  # 用例数据
        self.suffix_request_params = None  # 存储处理后的请求参数
        self.suffix_response = None  # 存储当前响应内容
        self.test_module = test_module  # 当前调用的test模块
        self.test_class = test_class  # 当前调用的test类

    def __repr__(self):
        return f'<RequestUtil 用例名称={self.case_name}>'

    @classmethod
    def verify_data(cls, val):
        """请求参数验证的逻辑"""
        # 用例数据是字典的校验
        error_msg = ''
        if not isinstance(val, dict):
            error_msg = '请求参数校验失败，yaml用例不是json字典'
        else:
            # 用例数据的一级关键字校验
            # one_mp = ('name', 'request', 'validate')
            one_mp = ('name', 'request')
            one_keys = val.keys()
            if not all(1 if k in one_keys else 0 for k in one_mp):
                error_msg = f'请求参数校验失败，yaml用例中，必须包含一级关键字:{one_mp}'
            else:
                # 用例数据的二级关键字校验
                two_mp = ('method', 'url', 'base_url')
                two_keys = val.get('request').keys()
                if not all(1 if k in two_keys else 0 for k in two_mp):
                    error_msg = f'请求参数校验失败，yaml用例中，request必须包含二级关键字:{two_mp}'
        # 有报错，则写入日志，并抛出异常
        if error_msg:
            logger.info(f'<Exception>:{error_msg}')
            raise Exception(error_msg)

    @property
    def case_data(self):
        return self.__case_data

    @case_data.setter
    def case_data(self, val):
        try:
            self.verify_data(val)
        except:
            logger.info(self.end_msg)
            raise
        else:
            self.__case_data = val

    def send_request(self):
        # 1.处理参数
        try:
            # 逐个读取用例参数
            method = self.case_data.get('request').pop('method').lower()
            logger.info(f"请求方式：{method}")

            hot_load_obj = HotLoad()
            # 热加载处理base_url
            base_url = self.case_data.get('request').pop('base_url')
            base_url = hot_load_obj.replace_get_value(base_url)
            # 热加载处理url
            url = self.case_data.get('request').pop('url')
            url = hot_load_obj.replace_get_value(url)
            full_url = base_url + url
            logger.info(f"请求路径：{full_url}")
            # 热加载处理: headers+json/params等参数
            kwargs = self.case_data.get('request')
            logger.info(f"原始参数：{kwargs}")
            kwargs = hot_load_obj.replace_get_value(kwargs)
            headers = kwargs.pop('headers')
            headers['User-Agent'] = self.UA
            logger.info(f"headers参数：{headers}")
            # 处理文件参数
            file_path = kwargs.get('files')
            if file_path is not None:
                kwargs.update(files=open(file_path, 'rb'))

            list(map(logger.info, list(f'{k}参数: {v}' for k, v in kwargs.items())))
            # 把热加载回填后的请求参数，存起来
            self.suffix_request_params = kwargs
        except Exception as e:
            params_error = f"请求前报错，<{e.__class__.__name__}>:{e}"
            logger.info(params_error)
            raise Exception(params_error)

        # 2.发起请求
        try:
            # 禁用所有警告
            warnings.filterwarnings("ignore")
            resp = RequestUtil.session.request(method, full_url, verify=False, **kwargs)
            logger.info(f'响应状态码： {resp.status_code}')
            logger.info(f'响应结果: {resp.text}')
            # 把当前响应结果，存起来
            self.suffix_response = resp
        except Exception as e:
            send_error = f"请求时报错，<{e.__class__.__name__}>:{e}"
            logger.info(send_error)
            raise Exception(send_error)
        # 3.断言处理
        # 4.提取保存全局变量（若有）

    def validate(self):
        # 3.断言处理
        validate_list = self.case_data.get('validate')
        try:
            validate_check(validate_list, self.suffix_response, self.suffix_request_params)
        except Exception as e:
            assert_error = f'断言报错，<{e.__class__.__name__}>:{e}'
            logger.info(assert_error)
            raise


if __name__ == '__main__':
    pass
